package idv.arthur.work.deployment;

import idv.arthur.work.Widgets;
import idv.arthur.work.strucs.WebDep;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.PrintStream;
import java.io.StringReader;
import java.io.UnsupportedEncodingException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Timestamp;
import java.util.ArrayList;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.net.telnet.TelnetClient;
import org.tmatesoft.svn.core.SVNProperties;
import org.tmatesoft.svn.core.SVNURL;
import org.tmatesoft.svn.core.auth.ISVNAuthenticationManager;
import org.tmatesoft.svn.core.io.SVNRepository;
import org.tmatesoft.svn.core.io.SVNRepositoryFactory;
import org.tmatesoft.svn.core.wc.SVNWCUtil;

import com.google.gson.Gson;
import com.google.gson.stream.JsonReader;
import com.jcraft.jsch.ChannelSftp;
import com.jcraft.jsch.JSch;
import com.jcraft.jsch.JSchException;
import com.jcraft.jsch.Session;

public class RedepBySingleFile extends HttpServlet {
	private static final long serialVersionUID = 1L;
	
	static Widgets wid = new Widgets();
	static TelnetClient telnet = null;
	static InputStream input = null;
	static PrintStream out = null;
	
    public RedepBySingleFile() {
        super();
    }
    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    	doPost(request,response);
    }

	protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
		
		request.setCharacterEncoding( wid.UIcodeSet );
		response.setContentType("application/json");
		response.setCharacterEncoding( wid.UIcodeSet );
		
		final boolean isContainedWeb = "2".equalsIgnoreCase( request.getParameter("filePortfolio") )?false:true;		////此單檔是否以web的方式處理
		final String currentUser = request.getParameter("loginUser");			//執行人員ID
		final String split = "##";
		final String bssId = "MBMS";
		final String subSysId = request.getParameter("subSysId");					//子系統
		final String envId = request.getParameter("envId");								//部署環境
		final String svnPath = request.getParameter("svnPath");						//SVN路徑
		final String comment = request.getParameter("comment");				//部署的原因
		final String fileName = request.getParameter("fileName");					//檔案名稱														
		final int fileVer= Integer.parseInt( request.getParameter("ver") );		//版次
		final String depPath = request.getParameter("depPath");					//延伸路徑
		final JsonReader reader =new JsonReader(new StringReader( request.getParameter("sqList") ));	//資源群組
		
		Connection conn = null;
		Connection tarConn = null;
		PreparedStatement ps = null;
		ResultSet rs = null;
		ResultSet rs2 = null;
		String errPars = "}";
		String tmpStr = null;
		
		/** 
		 * --手動部署時使用--
		 * 儲存這次部署，所有要部署的子系統和資源群組
		 * 格式為  subsysid:squadid
		 * */
		ArrayList<String> squadIdList = new ArrayList<String>();						

		JSch jsch = new JSch();
        JSch.setConfig("StrictHostKeyChecking", "no");
        Session session = null;
        ChannelSftp sftp = null;
        
		Timestamp bTime;
		String tmp = "";
		
		String res = "";
		String strSVNpar = "";
		
		String modulePath = "";	//模組在AP Server上放的位置
		String wcIP = "";
		String wcLoginId = "";
		String wcPWD = "";
		
		WebDep webDep = new WebDep();
		
		int deplmainSN = 0;
		int depmainSN = 0;
		StringBuilder strSQL = new StringBuilder();
		ISVNAuthenticationManager authManager = null;
		SVNRepository repository = null;
		SVNProperties fileProperties = new SVNProperties();
		ByteArrayOutputStream baos = null;
		
		reader.beginArray();
		while (reader.hasNext()) {
			reader.beginObject();
			reader.nextName();
			tmp = reader.nextString();	//資源群組ID
			reader.nextName();			//資源群組名稱
			reader.nextString();
			reader.nextName();			//資源群組路徑
			reader.nextString();
			boolean isNotExist = true;	//預設是不存在
			for (int i=0;i<squadIdList.size();i++) {
				if ( ( tmp ).equalsIgnoreCase(squadIdList.get(i)) ) {
					isNotExist = false;
				}
			}
			if (isNotExist) { squadIdList.add( tmp ); }
			reader.endObject();
		}
		reader.endArray();
		reader.close();
		
		
		try {
			conn = wid.getConn(true);
			final int localSysNo = wid.getLocalSysNo( conn, ps, rs);		//取得目前這個系統的身份
			final int tarSysNo = localSysNo;												//單檔重新部署必定目標系統即本機系統												
			/*
			 * 1. 先前的部署計畫中，在「相同子系統」中，是否存在，一樣內容的部署計畫
			 * 		何謂一樣？ <之後有空再做>
			 * **/ 
			
			deplmainSN = wid.getCurrentSN("deplmain", conn, ps, rs);
			strSQL.replace(0, strSQL.length(), "INSERT INTO deplmain (deplmainSN,sysSN,creator,createdDateTime,deplComment,subsysid) VALUES ("+deplmainSN+",?,?,?,?,?)" );
			 /* 部署計畫表頭		交付方  */
			ps = conn.prepareStatement( strSQL.toString() );
			ps.setInt(1, localSysNo);
			ps.setString(2, currentUser);
			ps.setTimestamp(3, wid.currentTimeStamp());
			ps.setString(4, wid.UI2DB( comment,conn.getMetaData().getDatabaseProductName() ) );
			ps.setString(5, subSysId );
			ps.execute();
			
			strSQL.replace(0, strSQL.length(), "INSERT INTO depldetail (deplmainSN,depldetailid,localFile,localVer,isnonweb,depPath,localPath,creator,createdDateTime) VALUES (?,?,?,?,?,?,?,?,?)");
			ps = conn.prepareStatement( strSQL.toString() );
			ps.setInt(1, deplmainSN );	
			ps.setInt(2, 1 );	//細項的流水號	
			ps.setString(3, wid.UI2DB( fileName,conn.getMetaData().getDatabaseProductName() ) );
			ps.setInt(4, fileVer );
			ps.setString(5, isContainedWeb?"f":"t"  );
			ps.setString(6, depPath );
			ps.setString(7, svnPath );
			ps.setString(8, currentUser);
			ps.setTimestamp(9,wid.currentTimeStamp());
			ps.execute();
			
			for ( int i=0; i<squadIdList.size() ; i++ ) {	//每個Squad 裡的主機群都要去置放欲部署的檔案
				strSQL.replace(0, strSQL.length(), "INSERT INTO mpdeplunitsqu (deplmainSN,depldetailid,bssid,subsysid,squadid,creator,createdDateTime) VALUES (?,?,?,?,?,?,?)");
				ps = conn.prepareStatement( strSQL.toString() );
				ps.setInt(1, deplmainSN);
				ps.setInt(2, 1);
				ps.setString(3, bssId);
				ps.setString(4, subSysId);
				ps.setString(5, squadIdList.get(i) );
				ps.setString(6, currentUser);
				ps.setTimestamp(7,wid.currentTimeStamp());
				ps.execute();
			}
				
			strSVNpar = wid.getTarSVNConnection(localSysNo, tarSysNo, conn, ps, rs,split );
	        repository = SVNRepositoryFactory.create(SVNURL.parseURIEncoded( strSVNpar.split(split)[2] + "/" + subSysId ) );
	        authManager = SVNWCUtil.createDefaultAuthenticationManager(strSVNpar.split(split)[0], strSVNpar.split(split)[1]);
	        repository.setAuthenticationManager(authManager);
		    
	        //部署記錄單頭	
			depmainSN = wid.getCurrentSN("depmain", conn, ps, rs);
			strSQL.replace(0, strSQL.length(), "INSERT INTO depmain (depmainSN,deplmainSN,envid,creator,createdDateTime) VALUES ("+depmainSN+",?,?,?,?)");
			ps = conn.prepareStatement( strSQL.toString() );
			ps.setInt(1, deplmainSN);
			ps.setString(2, envId);
			ps.setString(3, currentUser);
			ps.setTimestamp(4, wid.currentTimeStamp() );	
			ps.execute();
			
			//	新增部署記錄細項
			strSQL.replace(0, strSQL.length(), "INSERT INTO depdetail (depmainSN,depdetailid,deplmainSN,depldetailid) VALUES (?,?,?,?)");
			ps = conn.prepareStatement( strSQL.toString() );
			ps.setInt(1, depmainSN);
			ps.setInt(2, 1 );
			ps.setInt(3, deplmainSN);
			ps.setInt(4, 1 );
			ps.execute();
			
			if ( !isContainedWeb ) {
				for ( int i=0; i<squadIdList.size() ; i++ ) {	//每個Squad 裡的主機群都要去置放欲部署的檔案
					//開始查這個Squad 會去的主機群
					strSQL.replace( 0, strSQL.length(), "SELECT t1.ip,t1.port,t2.loginId,t2.loginPWD,t2.hostname,t3.squadpath FROM mpsquadhosts t1, hosts t2,squad t3 WHERE t1.bssid=t2.bssid AND t1.subsysid=t2.subsysid AND t1.port=t2.port AND t1.bssid=t3.bssid AND t1.subsysid=t3.subsysid AND t1.squadid=t3.squadid  AND t1.bssid=? AND t1.subsysid=? AND t1.envid=? AND t1.squadid=?");
					ps = conn.prepareStatement( strSQL.toString() );
					ps.setString(1, bssId );
					ps.setString(2, subSysId );
					ps.setString(3, envId);
					ps.setString(4, squadIdList.get(i) );
					rs = ps.executeQuery();
					
					while (rs.next()) {
						bTime = wid.currentTimeStamp();	//實體部署的啟始時間
						/*
						 * 實體部署
						 * */
						baos = new ByteArrayOutputStream();
						
						if ( "".equalsIgnoreCase( svnPath ) ) {
							tmpStr = repository.getRepositoryPath("")+"/"+svnPath;
						} else {
							tmpStr= repository.getRepositoryPath("")+"/"+svnPath +"/"+fileName;
						}
						
						repository.getFile( tmpStr, fileVer, fileProperties, baos);
						
						session = jsch.getSession( rs.getString("loginId"), rs.getString("ip") );
						session.setPassword( rs.getString("loginPWD") );
				        session.connect();
				        sftp = (ChannelSftp) session.openChannel("sftp");
				        
				        sftp.connect();
						if ( sftp.isConnected() ) {
							
							/*	改變資源群組的路徑	*/
							if ( !"".equalsIgnoreCase( rs.getString("squadpath") )) {
								wid.cwd( sftp,rs.getString("squadpath") );
							} 
							
							/*	改變檔案部署的路徑	*/
							if (!"".equalsIgnoreCase( depPath )) {
								if ( depPath.startsWith("/") ) {
									tmp= depPath.substring(1, depPath.length() );
								}
								wid.cwd(sftp, tmp);
							}
							
							// 如果傳的是// war要加這行
							input = new ByteArrayInputStream(baos.toByteArray());
							sftp.put(input, fileName, ChannelSftp.OVERWRITE);
							
							/** 改變檔案的權限，因為要執行 */
							sftp.chmod( wid.ACCESSRIGHT, fileName );
							
							/*	寫入Log	*/
							strSQL.replace(0, strSQL.length(), "INSERT INTO itemdeplog (depmainSN,depdetailid,bssid,subsysid,envid,squadid,ip,port,squadpath,depRes,bTime,eTime,depslog) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
							ps = conn.prepareStatement( strSQL.toString() );
							
							ps.setInt(1, depmainSN);
							ps.setInt(2, 1 );
							ps.setString(3, bssId);
							ps.setString(4, subSysId );
							ps.setString(5, envId);
							ps.setString(6, squadIdList.get(i) );
							ps.setString(7, rs.getString("ip") );
							ps.setInt(8, rs.getInt("port") );
							ps.setString(9, rs.getString("squadpath") );
							ps.setInt(10, 1);
							ps.setTimestamp(11, bTime);
							ps.setTimestamp(12, wid.currentTimeStamp());
							ps.setString(13, "");
							ps.execute();
							
							res += fileName+"\t 大小:"+baos.size()+" bytes\t部署於  "+wid.DB2UI( rs.getString("hostname"),conn.getMetaData().getDatabaseProductName() )+".......完成\n";
						} else {
							res += "無法登入主機+"+rs.getString("hostname")+"!!";
							System.out.println(res);
						}
					}	
				}
			} else {		//web
				strSQL.replace(0, strSQL.length(), "SELECT t2.webconsoleip,t2.loginid,t2.pwd,t2.modulepath,t1.envid,t1.envname,t1.preClass FROM env t1,wcl t2 WHERE t1.webconsolesn=t2.webconsolesn AND t1.envid=? AND t1.subsysid=? AND t1.bssid = ?");
				ps = conn.prepareStatement( strSQL.toString() );
				ps.setString(1, envId );
				ps.setString(2, subSysId );
				ps.setString(3, bssId );
				rs = ps.executeQuery();
				rs.next();
				wcIP = rs.getString("webconsoleip");
				modulePath = rs.getString("modulepath");
				wcLoginId = rs.getString("loginid");
				wcPWD = rs.getString("pwd");
				
				webDep.setModuleName( fileName.substring( 0, wid.DB2UI(fileName,conn.getMetaData().getDatabaseProductName()).lastIndexOf(".") ) );	//只取檔名的部份，故用. 來做lastIndexOf
				webDep.setExt( fileName.substring( wid.DB2UI(fileName,conn.getMetaData().getDatabaseProductName()).lastIndexOf("."), wid.DB2UI(fileName,conn.getMetaData().getDatabaseProductName()).length() ) );
				webDep.setSquads(squadIdList);
				
				bTime = wid.currentTimeStamp();	//實體部署的啟始時間
				
				// 檔案放去 AP Server
				baos = new ByteArrayOutputStream();
				
				if ( "".equalsIgnoreCase( svnPath ) ) {
					tmpStr = repository.getRepositoryPath("")+"/"+fileName;
				} else {
					tmpStr= repository.getRepositoryPath("")+"/"+svnPath +"/"+fileName;
				}
				repository.getFile( tmpStr, fileVer, fileProperties, baos);
				
				session = jsch.getSession( wcLoginId, wcIP );
				session.setPassword( wcPWD );
		        session.connect();
		        sftp = (ChannelSftp) session.openChannel("sftp");
		        sftp.connect();
				if ( sftp.isConnected() ) {
					
					/*	切換到AP Server放模組地方的路徑	*/
					if (!"".equalsIgnoreCase( modulePath )) {
						wid.cwd( sftp, modulePath );
					} 
					
					input = new ByteArrayInputStream(baos.toByteArray());
					sftp.put(input, fileName, ChannelSftp.OVERWRITE);
					
					/** 改變檔案的權限，因為要執行 */
					sftp.chmod( wid.ACCESSRIGHT, fileName );
					
					/*	寫入Log	*/
					strSQL.replace(0, strSQL.length(), "INSERT INTO itemdeplog (depmainSN,depdetailid,bssid,subsysid,envid,squadid,ip,port,squadpath,depRes,bTime,eTime,depslog) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?)");
					ps = conn.prepareStatement( strSQL.toString() );
					
					ps.setInt(1, depmainSN);
					ps.setInt(2, 1 );
					ps.setString(3, bssId);
					ps.setString(4, subSysId );
					ps.setString(5, envId);
					
					ps.setString(7, wcIP );
					ps.setInt(8, 7382 );								// web的程式 port都寫成7382
					ps.setString(9, modulePath );				// 原本的squadPath，皆寫入目前設定的 AP Server放模組地方的路徑
					ps.setInt(10, 1);
					ps.setTimestamp(11, bTime );
					ps.setTimestamp(12, wid.currentTimeStamp() );
					ps.setString(13, "");
					for ( int i=0; i<squadIdList.size() ; i++ ) {
						ps.setString(6, squadIdList.get(i) );
						ps.execute();
					}
					res += "更新檔案完成\n";
				} else {
					res += "無法登入主機!!";
					System.out.println(res);
				}
			}		
			
			if ( isContainedWeb ) {
				/*	檔案放到AP Server了，現在要起動它	*/
				telnet = new TelnetClient();
				telnet.connect(wcIP, 23);
				telnet.setSoTimeout( 1200000 );
	            out = new PrintStream( telnet.getOutputStream() );
	            input = telnet.getInputStream();
	            wid.readUntil("login: ", input);
	            wid.write( wcLoginId, out );
	            wid.readUntil("Password: ", input);
	            wid.write( wcPWD, out);
	            wid.sendCommand("cd " + modulePath,input, out);  //切換到部署目錄
				
	            /**
				 * j代表的是程式所要去的 squad
				 */
				String squads = "";
				for (int j=0; j < webDep.getSquads().size(); j++ ) {
					squads += webDep.getSquads().get(j)+",";
				}
				squads = squads.substring( 0, squads.length()-1 ); //-1是為了扣掉那個 ,
				
				String deployCommand = wid.composeCommand(wcIP, wcLoginId, wcPWD, webDep.getModuleName(),webDep.getModuleName()+"."+webDep.getExt(), squads, "redeploy");  //組合部署指令
            	
				wid.sendCommand( deployCommand,input,out );  //執行部署指令
                String ret = wid.readUntil(modulePath + "> ",input);
                res = wid.splitTokenONE+wid.deployInfo(ret);
                
                res += "\n"+webDep.getModuleName();
                
                if ( !"Redeploy successfully.".equalsIgnoreCase( wid.deployInfo(ret) ) ) {
                	errPars = wid.toJend("result", res);
        			wid.kem(conn, ps, new Exception(), wid.toEType("Bug Trace")+errPars, false);
                } else {
                	res = "部署完成";
                }
			}
			
		
		} catch ( UnsupportedEncodingException e ) {
			res = wid.kem(conn, ps, e, wid.toEType("UnsupportedEncodingException")+errPars, wid.isPrint2Console );
		} catch ( SQLException e) {
			res = wid.kem(conn, ps, e, wid.toEType("SQLException")+errPars, wid.isPrint2Console );
		} catch ( JSchException e) {
			res = wid.kem(conn, ps, e, wid.toEType("JSchException")+errPars, wid.isPrint2Console );
			if (res.contains("Auth fail")) {
				res = "帳密有誤!\n無法登入\n"+strSVNpar.split(split)[2];		
			}
		} catch ( Exception e) {
			if ( res.contains( wid.splitTokenONE ) ) {
				res = res.substring(wid.splitTokenONE.length(),res.length());
			} else {
				res = wid.kem(conn, ps, e, wid.toEType("Exception")+errPars, wid.isPrint2Console );
			}
		} finally {
			try {
				if ( input != null ) { input.close(); }
				if ( out != null ) {
					wid.write("exit",out);
					out.close();
				}
				if ( baos != null ) { baos.close(); }
				if ( rs != null ) {rs.close();}
				if ( rs2 != null ) {rs2.close();}
				if ( ps != null ) {ps.close();}
				if ( conn != null ) {conn.close();}
				if ( tarConn != null ) {tarConn.close();}
				if ( telnet != null ) { if ( telnet.isConnected() ) { telnet.disconnect(); } }
				if ( sftp != null ) { if ( sftp.isConnected() ) { sftp.disconnect(); } }
				if ( session != null ) { if ( session.isConnected() ) { session.disconnect(); } }
			} catch (SQLException e) {
				System.out.println( wid.errProcess(e) );
			}
		}
		
		response.getWriter().write(new Gson().toJson(res));
	}	
}